
#include "fifo.h"


uint8_t fifo_check_error(FIFO_ST* fifo)
{
    if((fifo->tail >= fifo->len)||
        (fifo->tail >= fifo->len)||
		(fifo->buff == 0)||
		((fifo->head_out_of_range != fifo->tail_out_of_range)&&(fifo->head_out_of_range != ~fifo->tail_out_of_range))){
        return 0;
    }
    else{
        return 1;
    }
}

uint16_t fifo_get_free_len(FIFO_ST* fifo)
{
    uint16_t free = 0;
	if(fifo->head >= fifo->tail){
		free = fifo->len - (fifo->head - fifo->tail);
	}
	else{
		//free = fifo->len - (fifo->head - fifo->tail) --> free = fifo->len - ((fifo->len + fifo->head) - fifo->tail)
		free = fifo->tail - fifo->head;
	}

	if((free == fifo->len)&&
	   (fifo->head_out_of_range != fifo->tail_out_of_range)){
	   free = 0;
	}

	return free;
}

uint16_t fifo_get_used_len(FIFO_ST* fifo)
{
	return (fifo->len - fifo_get_free_len(fifo));
}

void fifo_reset(FIFO_ST *fifo)
{
    fifo->head = 0;
    fifo->tail = 0;
    fifo->head_out_of_range = 0;
    fifo->tail_out_of_range = 0;
}

void fifo_write(FIFO_ST* fifo, uint8_t dat)
{                              //not full
    fifo->buff[fifo->head] = dat;

    if((fifo->head+1) >= fifo->len){    //fifo->tail > fifo->len: tail pointer out of range
        fifo->head_out_of_range = ~fifo->head_out_of_range;
        fifo->head = 0;
    }
	else{
    	fifo->head++;
	}
}

void fifo_read(FIFO_ST* fifo, uint8_t* dat)
{                               //not empty
    *dat = fifo->buff[fifo->tail];

    if((fifo->tail+1) >= fifo->len){    //fifo->head > fifo->len: head pointer out of range
        fifo->tail_out_of_range = ~fifo->tail_out_of_range;
        fifo->tail = 0;
    }
	else{
    	fifo->tail++;
	}
}

uint8_t fifo_write_data(FIFO_ST* fifo, uint16_t data_len , uint8_t* in_data)
{
	uint16_t free;

    if((fifo == 0)||(data_len == 0)||(in_data == 0)) {
        return 0;
    }

    free = fifo_get_free_len(fifo);
    if(free == 0){
        return 0;
    }
    else if(free < data_len){
        return 0;
    }
    else{
        for(; data_len > 0; in_data++){
            fifo_write(fifo, *in_data);
            data_len--;
        }
        return 1;
    }
}

uint8_t fifo_read_data(FIFO_ST* fifo, uint16_t data_len, uint8_t* out_data)
{
	uint16_t free;

    if((fifo == 0)||(data_len == 0)||(out_data == 0)) {
        return 0;
    }

    free = fifo_get_free_len(fifo);
    if(fifo->len == free){
        return 0;
    }
    else if(fifo->len - free < data_len){
        return 0;
    }
    else{
        for(; data_len > 0; out_data++){
            fifo_read(fifo, out_data);
            data_len--;
        }
        return 1;
    }
}

uint8_t fifo_init(FIFO_ST *fifo, uint16_t buff_space_len, uint8_t* buff_space)
{
    if((fifo == 0)||(buff_space_len == 0)||(buff_space == 0)) {
        return 0;
    }

    fifo->buff = buff_space;
    fifo->len = buff_space_len;

    fifo->head = 0;
    fifo->tail = 0;
    fifo->head_out_of_range = 0;
    fifo->tail_out_of_range = 0;

    return 1;
}

